// Copyright (C) 2010 David Sugar, Tycho Softworks.
//
// This file is part of GNU uCommon C++.
//
// GNU uCommon C++ is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// GNU uCommon C++ is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with GNU uCommon C++.  If not, see <http://www.gnu.org/licenses/>.


#include <apennine-config.h>
#include <ucommon/ucommon.h>
#include <ucommon/secure.h>
#include <ucommon/export.h>
#include <apennine/namespace.h>
#include <apennine/xmlrpc.h>

namespace apennine {

static const char *idstr = "xmlrpc/" VERSION;

static void system_identity(OrderedIndex& index, XMLRPC *msg)
{
    if(msg->params() != 0) {
        msg->failure(3, "Invalid Parameters");
        return;
    }

    msg->success(idstr);
}

static void system_methods(OrderedIndex& index, XMLRPC *msg)
{
    if(msg->params()) {
        msg->failure(3, "Invalid Parameters");
        return;
    }

    msg->reply("^[");

    linked_pointer<XMLRPC::group> gp = index.begin();
    while(is(gp)) {
        XMLRPC::methods_t list = gp->members;
        while(list->name) {
            msg->reply("!s", list->name);
            ++list;
        }
        ++gp;
    }

    msg->reply("]");
}

static void system_help(OrderedIndex& index, XMLRPC *msg)
{
    if(msg->params() != 1) {
        msg->failure(3, "Invalid Parameters");
        return;
    }

    const char *method = msg->value(1, "string");
    if(!method || !*method) {
        msg->failure(4, "Invalid Method Argument");
        return;
    }

    linked_pointer<XMLRPC::group> gp = index.begin();

    while(is(gp)) {
        XMLRPC::methods_t list = gp->members;

        while(list->name) {
            if(eq(list->name, method)) {
                msg->reply("^s", list->help);
                return;
            }
            ++list;
        }

        ++gp;
    }

    msg->failure(4, "Unknown Method");
}

static void system_signature(OrderedIndex& index, XMLRPC *msg)
{
    if(msg->params() != 1) {
        msg->failure(3, "Invalid Parameters");
        return;
    }

    const char *method = msg->value(1, "string");
    if(!method || !*method) {
        msg->failure(4, "Invalid Method Argument");
        return;
    }

    linked_pointer<XMLRPC::group> gp = index.begin();
    while(is(gp)) {
        XMLRPC::methods_t list = gp->members;

        while(list->name) {
            if(eq(list->name, method)) {
                msg->reply("^[!s]", list->signature);
                return;
            }
            ++list;
        }

        ++gp;
    }

    msg->failure(4, "Unknown Method");
}

XMLRPC::define_t XMLRPC::system[] = {
    {"system.identity", &system_identity,
        "Identify server type and version", "string"},
    {"system.listMethods", &system_methods,
        "List server methods", "array"},
    {"system.methodHelp", &system_help,
        "Get help text for specified method", "string, string"},
    {"system.methodSignature", &system_signature,
        "Get parameter signature for specified method", "array, string"},
    {NULL, NULL, NULL, NULL}};

OrderedIndex XMLRPC::common;

XMLRPC::group::group(OrderedIndex& index, struct define *list, const char *prefix) :
OrderedObject(&index)
{
    members = list;
    methods = prefix;
}

XMLRPC::group::group(struct define *list, const char *prefix) :
OrderedObject(&common)
{
    members = list;
    methods = prefix;
}

void XMLRPC::id(const char *string)
{
    idstr = string;
}

void XMLRPC::process(OrderedIndex& index, XMLRPC *msg)
{
    const char *method = msg->id();

    linked_pointer<group> gp = index.begin();
    while(is(gp)) {
        XMLRPC::methods_t list = gp->members;

        if(eq(method, gp->methods, strlen(gp->methods))) {
            while(list->name) {
                if(eq(list->name, method)) {
                    list->dispatch(index, msg);
                    return;
                }
                ++list;
            }
        }
        ++gp;
    }
}

static XMLRPC::group _system(XMLRPC::system, "system.");

} // end namespace
